package cn.acmenms.utils;

import cn.acmenms.po.enums.TimeFormatEnum;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.Period;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;

/**
 * @ClassName: DateUtil
 * @Description: 基于 JDK 8 time包的时间工具类
 * @author: 神奇的鸭鸭
 * @Date: 2017/12/28 9:54
 */
public final class LocalDateTimeUtils {
    /**
     * 获取默认时间格式: yyyy-MM-dd HH:mm:ss
     */
    private static final DateTimeFormatter DEFAULT_DATETIME_FORMATTER = TimeFormatEnum.YYYY_MM_DD_HHMMSS.getFormatter();

    /**
     * 毫秒值
     */
    private static final long ONE_DAY = 86400000L; // 24*60*60*1000
    private static final long ONE_HOUR = 3600000L; // 60*60*1000
    private static final long ONE_MINUTE = 60000L; // 60*1000

    /**
     * 几天前
     */
    private static final int AGO_DAY_0 = 0, AGO_DAY_1 = 1, AGO_DAY_2 = 2, AGO_DAY_7 = 7;

    /**
     * 状态
     */
    private static final String JUST_NOW = "刚刚";
    private static final String ONE_MINUTE_AGO = "分钟前";
    private static final String ONE_HOUR_AGO = "小时前";
    private static final String ONE_DAY_AGO = "天前";
    private static final String TWO_DAY_AGO = "昨天";
    private static final String THTEE_DAY_AGO = "前天";



    private LocalDateTimeUtils(){}

    /**
     * 时间字符串转时间类
     * @param timeStr 时间字符串
     * @return LocalDateTime
     */
    public static LocalDateTime parseTime(String timeStr){
        return LocalDateTime.parse(timeStr, DEFAULT_DATETIME_FORMATTER);
    }

    /**
     * 周期
     */
    private static final String[] WEEKCN = { "", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期天" };

    /**
     * 时间字符串转时间类-指定格式化格式
     * @param timeStr 时间字符串
     * @param format 格式化格式
     * @return LocalDateTime
     */
    public static LocalDateTime parseTime(String timeStr, TimeFormatEnum format) {
        return LocalDateTime.parse(timeStr, format.getFormatter());
    }

    /**
     * 时间类转时间字符串
     * @param time 时间对象
     * @return 时间字符串
     */
    public static String parseTime(LocalDateTime time) {
        return DEFAULT_DATETIME_FORMATTER.format(time);
    }

    /**
     * 时间类转时间字符串-指定格式化格式
     * @param time 时间对象
     * @param format 格式化格式
     * @return 时间字符串
     */
    public static String parseTime(LocalDateTime time, TimeFormatEnum format) {
        return format.getFormatter().format(time);
    }

    /**
     * 获取当前时间字符串
     * @return 时间字符串
     */
    public static String getCurrentDatetime() {
        return DEFAULT_DATETIME_FORMATTER.format(LocalDateTime.now());
    }

    /**
     * 获取指定日期的毫秒值
     * @param time 时间对象
     * @return 毫秒值
     */
    public static Long getMilliByTime(LocalDateTime time) {
        return time.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
    }

    /**
     * 获取指定日期的秒
     * @param time 时间对象
     * @return 秒值
     */
    public static Long getSecondsByTime(LocalDateTime time) {
        return time.atZone(ZoneId.systemDefault()).toInstant().getEpochSecond();
    }

    /**
     * 日期加上一个数,根据field不同加不同值,field为ChronoUnit.*
     * @param time 时间对象
     * @param number 增量
     * @param field 单位
     * @return 新时间对象
     */
    public static LocalDateTime plus(LocalDateTime time, long number, ChronoUnit field) {
        return time.plus(number, field); // field ChronoUnit.*
    }

    /**
     * 日期减去一个数,根据field不同减不同值,field参数为ChronoUnit.*
     * @param time 时间对象
     * @param number 减量
     * @param field 单位
     * @return 新时间对象
     */
    public static LocalDateTime minu(LocalDateTime time, long number, ChronoUnit field){
        return time.minus(number,field); // field ChronoUnit.*
    }

    /**
     * 获取两个日期的差 field参数为ChronoUnit.*
     * @param startTime 起始日期
     * @param endTime 结束日期
     * @param field 返回单位
     * @return 时间差-单位field
     */
    public static long beWeenTwoTime(LocalDateTime startTime, LocalDateTime endTime, ChronoUnit field){
        Period period = Period.between(LocalDate.from(startTime), LocalDate.from(endTime));
        if (field == ChronoUnit.YEARS) return period.getYears();
        if (field == ChronoUnit.MONTHS) return period.getYears() * 12 + period.getMonths();
        return field.between(startTime, endTime);
    }

    /**
     * 友好时间格式化
     * @param sourceDate 时间对象
     * @return 友好时间字符串
     */
    public static String friendlyTime(LocalDateTime sourceDate) {
        // 获取当前日期和传入日期的时间差-毫秒值
        long millise = LocalDateTimeUtils.beWeenTwoTime(sourceDate, LocalDateTime.now(), ChronoUnit.MILLIS);
        // 按天计算
        long daysAgo = millise / ONE_DAY;
        if(daysAgo == AGO_DAY_0) { // 今天
            return LocalDateTimeUtils.getCurDayInfo(millise);
        } else if(daysAgo == AGO_DAY_1) { // 昨天
            return TWO_DAY_AGO + " " + getHourAndMin(sourceDate);
        } else if(daysAgo == AGO_DAY_2) { // 前天
            return THTEE_DAY_AGO + " " + LocalDateTimeUtils.getHourAndMin(sourceDate);
        } else if(daysAgo > AGO_DAY_2 && daysAgo <= AGO_DAY_7) { // 几天前
            return daysAgo + ONE_DAY_AGO;
        } else { // 超过一个星期不格式化 直接返回 yyyy-MM-dd mm:hh:ss
            return LocalDateTimeUtils.parseTime(sourceDate);
        }
    }

    /**
     * 友好时间格式化——对一天内的时间进行格式化
     * @param millise 小于86400000L(一天)的毫秒值
     * @return 友好时间字符串
     */
    private static String getCurDayInfo(long millise) {
        if(millise / ONE_MINUTE < 0) { // 刚刚
            return JUST_NOW;
        } else if (millise / ONE_MINUTE > 0 && millise / ONE_HOUR == 0) { // 几分钟前
            return millise / ONE_MINUTE + ONE_MINUTE_AGO;
        } else { // 几小时前
            return millise / ONE_HOUR + ONE_HOUR_AGO;
        }
    }

    /**
     * 友好的时间格式化——得到日期的小时和分钟
     * @param sourceDate 时间对象-这个日期永远小于当前时间
     * @return 当前日期的小时和分钟 hh:ss
     */
    public static String getHourAndMin(LocalDateTime sourceDate) {
        String timeStr = LocalDateTimeUtils.parseTime(sourceDate);
        return timeStr.substring(timeStr.length()-5,timeStr.length());
    }

    /**
     * 获取当前日期是星期几
     * @param now 当前日期
     * @return 星期几
     */
    public static String getWeekCN(LocalDateTime now) {
        int value = now.getDayOfWeek().getValue();
        return WEEKCN[value];
    }

    /**
     * 根据 年份 月份 获取一个月的总天数
     * @param year 年份
     * @param month 月份
     * @return 当前月份的总天数
     */
    public static Integer getMonthTotalDay(Integer year, Integer month) {
        return LocalDate.of(year, month, 1).lengthOfMonth();
    }

    /**
     * 根据 年份 月份 获取上个月的总天数
     * @param year 年份
     * @param month 月份
     * @return 当前月份的总天数
     */
    public static Integer getBeforeMonthTotalDay(Integer year, Integer month) {
        return LocalDate.of(year, month, 1).minus(1, ChronoUnit.MONTHS).lengthOfMonth();
    }

    /**
     * 根据 年份 月份 获取一个月的第一天
     * @param year 年份
     * @param month 月份
     * @return 当前月的第一天是周几(1-7)
     */
    public static Integer getMonthFristDay(Integer year, Integer month) {
        return LocalDate.of(year, month, 1).getDayOfWeek().getValue();
    }

    /**
     * 判断时间字符串是否在当天之前
     * @param timeStr 时间字符串
     * @return true/false
     */
    public static Boolean beforeNowDate(String timeStr, TimeFormatEnum format) {
        return LocalDateTimeUtils.parseTime(timeStr, format).isAfter(LocalDateTime.now());
    }
}